// -----------------------------
// Author: 		Dhaenens Gert 
// Creation: 	8/20/2013 7:31:23 AM				
// -----------------------------

#ifndef _LuaClassInl_h
#define _LuaClassInl_h

#include "LuaObject.h"
#include "LuaRef.h"
#include "LuaStackGuard.h"
#include "Lua.h"
#include "LuaStack.h"
#include "LuaMethod.h"
#include "LuaDataType.h"
#include "LuaClassRegister.h"

namespace ObjectLua
{

	template<typename Class>
	char LuaClass<Class>::s_ClassName[MAX_CLASS_NAME_LENGTH];
	template<typename Class>
	Lua* LuaClass<Class>::s_lpLua = nullptr;
	template<typename Class>
	LuaTable LuaClass<Class>::s_MetaTable;
	template<typename Class>
	LuaDataList<Class> LuaClass<Class>::s_Datamembers;
	template<typename Class>
	LuaStaticDataList<Class> LuaClass<Class>::s_StaticDataMembers;


	template<typename Class>
	LuaClass<Class>::LuaClass(Lua* lpLua)
		: LuaObject(lpLua)
	{
	}
	template<typename Class>
	LuaClass<Class>::~LuaClass()
	{
	}

	template<typename Class>
	LuaClass<Class> LuaClass<Class>::bind(Lua* lpLua, const char* name)
	{
		LUA_ASSERT(lpLua);
		LUA_ASSERT(name);
		LuaStackGuard stackGuard(lpLua);

		// Create the Name
		size_t nameLen = strnlen_s(name,512) + 1;
		strcpy_s(s_ClassName, nameLen, name);

		// Save the Lua Object
		s_lpLua = lpLua;

		// Request a meta table
		LuaTable table = s_lpLua->getGlobals()->addTable(s_ClassName);
		s_MetaTable = s_lpLua->getGlobals()->addMetaTable(s_ClassName);

		// Set the Table Metatable
		table.push();
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		s_MetaTable.push();

		// Add the __index
		s_lpLua->getStack()->pushString("__index");
		s_lpLua->getStack()->pushFunction(proxy_index);
		s_lpLua->getStack()->setTable(-3);

		// Add the __newindex
		s_lpLua->getStack()->pushString("__newindex");
		s_lpLua->getStack()->pushFunction(proxy_newindex);
		s_lpLua->getStack()->setTable(-3);


		// Save the Register
		LuaClassRegister<Class>::s_IsRegistered = true;
		LuaClassRegister<Class>::s_MetaTable = &s_MetaTable;
		strcpy_s(LuaClassRegister<Class>::s_MetaTableName, LuaClassRegister<Class>::MaxNameLength, s_ClassName);

		return LuaClass<Class>(s_lpLua);
	}

	template<typename Class>
	int LuaClass<Class>::proxy_index(lua_State* lpState)
	{
		// Get the Name of the Field
		const char* lpFieldName = s_lpLua->getStack()->getString(-1);

		// Check if userdata is passed, if so, get it
		Class* lpObject = nullptr;
		if(s_lpLua->getStack()->isUserData(-2))
		{
			LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->getUserData(-2));
			lpObject = gcObject->Object;
		}

		// If we have an Object
		// Check if there is a datamember bound
		if(lpObject)
		{
			BaseLuaDataMember<Class>* lpMember = s_Datamembers.getMember(lpFieldName);
			if(lpMember != nullptr)
			{
				lpMember->getValue(s_lpLua, lpObject);
				return 1;
			}
		}
		// Else check if we have a static datamember bound
		else
		{
			BaseLuaStaticDataMember<Class>* lpMember = s_StaticDataMembers.getMember(lpFieldName);
			if(lpMember != nullptr)
			{
				lpMember->getValue(s_lpLua);
				return 1;
			}
		}

		// If it is a function, push the function
		// from the metatable
		s_MetaTable.getField(lpFieldName);
		if(s_lpLua->getStack()->isFunction(-1))
			return 1;
		else
			s_lpLua->getStack()->pop(-3);

		// Check if we have a base class
		{
			s_MetaTable.getField("__base");
			if(s_lpLua->getStack()->isTable(-1))
			{
				// We have a base class, get it's proxy method
				s_lpLua->getStack()->getField(-1, "__index");
				if(s_lpLua->getStack()->isFunction(-1))
				{
					// Get the Function from the stack
					LuaStack::CFunc lpFunc = s_lpLua->getStack()->getFunction(-1);
					s_lpLua->getStack()->pop(3);

					int returnCount = lpFunc(s_lpLua->getState());
					return returnCount;

				}
			}
		}

		return 0;
	}
	template<typename Class>
	int LuaClass<Class>::proxy_newindex(lua_State* lpState)
	{
		// Get the Name of the Field
		const char* lpFieldName = s_lpLua->getStack()->getString(-2);

		// Check if userdata is passed, if so, get it
		Class* lpObject = nullptr;
		if(s_lpLua->getStack()->isUserData(-3))
			lpObject = LuaDataType<Class*>::get(s_lpLua, -3);

		// If we have an Object
		// Check if there is a datamember bound
		if(lpObject)
		{
			BaseLuaDataMember<Class>* lpMember = s_Datamembers.getMember(lpFieldName);
			if(lpMember != nullptr)
			{
				if(lpMember->isReadOnly())
					s_lpLua->pushError("Trying to write to read-only datamember.");
				lpMember->setValue(s_lpLua, lpObject, -1);
				return 1;
			}
		}
		// Else check if we have a static datamember bound
		else
		{
			BaseLuaStaticDataMember<Class>* lpMember = s_StaticDataMembers.getMember(lpFieldName);
			if(lpMember != nullptr)
			{
				if(lpMember->isReadOnly())
					s_lpLua->pushError("Trying to write to read-only datamember.");
				lpMember->setValue(s_lpLua, -1);
				return 1;
			}
		}

		// Check if we have a base class
		{
			s_MetaTable.getField("__base");
			if(s_lpLua->getStack()->isTable(-1))
			{
				// We have a base class, get it's proxy method
				s_lpLua->getStack()->getField(-1, "__newindex");
				if(s_lpLua->getStack()->isFunction(-1))
				{
					// Get the Function from the stack
					LuaStack::CFunc lpFunc = s_lpLua->getStack()->getFunction(-1);
					s_lpLua->getStack()->pop(3);
					
					int returnCount = lpFunc(s_lpLua->getState());
					return returnCount;

				}
			}
		}


		return 0;
	}
	
	template<typename Class>
	template<typename ParentClass>
	void LuaClass<Class>::inheritFrom()
	{
		LuaStackGuard stackGuard(m_lpLua);

		if(!LuaClassRegister<ParentClass>::isRegistered())
			throw LuaException("Trying to inherit from non-registered class.");
		LuaTable* lpBaseTable = LuaClassRegister<ParentClass>::getMetaTable();

		// Request the Meta Table
		s_MetaTable.push();

		// Add the Base Field
		m_lpLua->getStack()->pushString("__base");
		lpBaseTable->push();
		m_lpLua->getStack()->setTable(-3);
	}
	template<typename Class>
	void LuaClass<Class>::addDestructor()
	{
		LuaStackGuard stackGuard(m_lpLua);

		// Request a meta table
		s_MetaTable.push();

		// Add the GC Field
		s_lpLua->getStack()->pushString("__gc");
		s_lpLua->getStack()->pushFunction(deleteObject);
		s_lpLua->getStack()->setTable(-3);

		// Add the Destroy Field
		s_lpLua->getStack()->pushString("destroy");
		s_lpLua->getStack()->pushFunction(forceDestroyObject);
		s_lpLua->getStack()->setTable(-3);

		// Add the keep Field
		s_lpLua->getStack()->pushString("keep");
		s_lpLua->getStack()->pushFunction(keepObject);
		s_lpLua->getStack()->setTable(-3);
	}

	/*
	//	Add a constructor to the Class
	//	0 arguments
	*/
	template<typename Class>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_0);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	1 arguments
	*/
	template<typename Class>
	template<typename P0>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_1<P0>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	2 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_2<P0,P1>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	3 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_3<P0,P1,P2>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	4 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_4<P0,P1,P2,P3>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	5 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_5<P0,P1,P2,P3,P4>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	6 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_6<P0,P1,P2,P3,P4,P5>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	7 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
	void LuaClass<Class>::addConstructor()
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__call");
		s_lpLua->getStack()->pushFunction(createObject_7<P0,P1,P2,P3,P4,P5,P6>);
		s_lpLua->getStack()->setTable(-3);
	}


	/*
	//	Add a constructor to the Class
	//	0 arguments
	*/
	template<typename Class>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_0);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	1 arguments
	*/
	template<typename Class>
	template<typename P0>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_1<P0>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	2 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_2<P0,P1>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	3 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_3<P0,P1,P2>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	4 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_4<P0,P1,P2,P3>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	5 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_5<P0,P1,P2,P3,P4>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	6 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_6<P0,P1,P2,P3,P4,P5>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add a constructor to the Class
	//	7 arguments
	*/
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
	void LuaClass<Class>::addProxyConstructor(const char* name)
	{
		LUA_ASSERT(name);

		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString(name);
		s_lpLua->getStack()->pushFunction(createObject_7<P0,P1,P2,P3,P4,P5,P6>);
		s_lpLua->getStack()->setTable(-3);
	}


	/*
	//	Add a method to the Class
	//	1 Return, 0 Arguments
	*/
	template<typename Class>
	template<typename R>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)())
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);

		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_0<Class,R>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 1 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_1<Class,R,P0>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 2 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_2<Class,R,P0,P1>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 3 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_3<Class,R,P0,P1,P2>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 4 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_4<Class,R,P0,P1,P2,P3>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 5 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_5<Class,R,P0,P1,P2,P3,P4>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 6 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_6<Class,R,P0,P1,P2,P3,P4,P5>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a method to the Class
	//	1 Return, 7 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5,P6))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::LuaMethod_7<Class,R,P0,P1,P2,P3,P4,P5,P6>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}



	/*
	//	Add a const method to the Class
	//	1 Return, 0 Arguments
	*/
	template<typename Class>
	template<typename R>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)() const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);

		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_0<Class,R>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 1 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_1<Class,R,P0>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 2 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_2<Class,R,P0,P1>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 3 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_3<Class,R,P0,P1,P2>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 4 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_4<Class,R,P0,P1,P2,P3>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 5 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_5<Class,R,P0,P1,P2,P3,P4>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 6 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_6<Class,R,P0,P1,P2,P3,P4,P5>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}
	/*
	//	Add a const method to the Class
	//	1 Return, 7 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
	void LuaClass<Class>::addMethod(const char* name, R (Class::*lpMethod)(P0,P1,P2,P3,P4,P5,P6) const)
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.push();
		Internal::ConstLuaMethod_7<Class,R,P0,P1,P2,P3,P4,P5,P6>::bind(m_lpLua,&s_MetaTable,name,lpMethod);
	}



	/*
	//	Add a Static Method to the Class
	//	1 Return, 0 Arguments
	*/
	template<typename Class>
	template<typename R>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)())
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 1 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 2 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0,P1))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 3 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 4 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 5 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3,P4))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 6 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3,P4,P5))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}
	/*
	//	Add a Static Method to the Class
	//	1 Return, 7 Arguments
	*/
	template<typename Class>
	template<typename R, typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
	void LuaClass<Class>::addStaticMethod(const char* name, R (*lpMethod)(P0,P1,P2,P3,P4,P5,P6))
	{
		LUA_ASSERT(name);
		LUA_ASSERT(lpMethod);
		LuaStackGuard stackGuard(m_lpLua);
		s_MetaTable.addFunction(name,lpMethod);
	}


	/*
	//	Add a datamember to the Class
	//	@ name : name of the datamember
	//	@ Datamember : pointer to the datamember
	//
	*/
	template<typename Class>
	template<typename T>
	void LuaClass<Class>::addDatamember(const char* name, T Class::*Datamember, bool bReadOnly)
	{
		BaseLuaDataMember<Class>* lpMember = new LuaDataMember<Class,T>(name, Datamember, bReadOnly);
		s_Datamembers.addMember(lpMember);
	}
	/*
	//	Add a static datamember to the Class
	//	@ name : name of the datamember
	//	@ Datamember : pointer to the datamember
	//
	*/
	template<typename Class>
	template<typename T>
	void LuaClass<Class>::addStaticDatamember(const char* name, T* Datamember, bool bReadOnly)
	{
		BaseLuaStaticDataMember<Class>* lpMember = new LuaStaticDataMember<Class,T>(name, Datamember, bReadOnly);
		s_StaticDataMembers.addMember(lpMember);
	}

	/*
	//	Add Unary Minus Operator
	*/
	template<typename Class>
	template<typename R>
	void LuaClass<Class>::addUnaryMinusOperator()
	{
		LuaStackGuard stackGuard(m_lpLua);
		
		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__unm");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::unaryMinus<R>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add Addition Operator
	*/
	template<typename Class>
	template<typename R, typename OtherClass>
	void LuaClass<Class>::addAdditionOperator()
	{
		LuaStackGuard stackGuard(m_lpLua);
		
		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__add");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::additionOperator<R,OtherClass>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add Subtraction Operator
	*/
	template<typename Class>
	template<typename R, typename OtherClass>
	void LuaClass<Class>::addSubtractionOperator()
	{
		LuaStackGuard stackGuard(m_lpLua);
		
		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__sub");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::subtractionOperator<R,OtherClass>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add Multiplication Operator
	*/
	template<typename Class>
	template<typename R, typename OtherClass>
	void LuaClass<Class>::addMultiplicationOperator()
	{
		LuaStackGuard stackGuard(m_lpLua);
		
		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__mul");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::multiplicationOperator<R,OtherClass>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add Division Operator
	*/
	template<typename Class>
	template<typename R, typename OtherClass>
	void LuaClass<Class>::addDivisionOperator()
	{
		LuaStackGuard stackGuard(m_lpLua);
		
		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__div");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::divisionOperator<R,OtherClass>);
		s_lpLua->getStack()->setTable(-3);
	}
	/*
	//	Add Index Operator
	*/
	template<typename Class>
	template<typename R, typename IndexType>
	void LuaClass<Class>::addIndexOperator()
	{
		LuaStackGuard stackGuard(m_lpLua);

		s_MetaTable.push();
		s_lpLua->getStack()->pushString("__ind");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::indexOperator<R,IndexType>);
		s_lpLua->getStack()->setTable(-3);

		s_lpLua->getStack()->pushString("__newind");
		s_lpLua->getStack()->pushFunction(LuaClass<Class>::newIndexOperator<R, IndexType>);
		s_lpLua->getStack()->setTable(-3);
	}



	template<typename Class>
	int LuaClass<Class>::createObject_0(lua_State* lpState)
	{
		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class();
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0>
	int LuaClass<Class>::createObject_1(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0, typename P1>
	int LuaClass<Class>::createObject_2(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);
		P1 arg1 = LuaDataType<P1>::get(s_lpLua, baseIndex + 2);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0, arg1);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0, typename P1, typename P2>
	int LuaClass<Class>::createObject_3(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);
		P1 arg1 = LuaDataType<P1>::get(s_lpLua, baseIndex + 2);
		P2 arg2 = LuaDataType<P2>::get(s_lpLua, baseIndex + 3);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0, arg1, arg2);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3>
	int LuaClass<Class>::createObject_4(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);
		P1 arg1 = LuaDataType<P1>::get(s_lpLua, baseIndex + 2);
		P2 arg2 = LuaDataType<P2>::get(s_lpLua, baseIndex + 3);
		P3 arg3 = LuaDataType<P3>::get(s_lpLua, baseIndex + 4);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0, arg1, arg2, arg3);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4>
	int LuaClass<Class>::createObject_5(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);
		P1 arg1 = LuaDataType<P1>::get(s_lpLua, baseIndex + 2);
		P2 arg2 = LuaDataType<P2>::get(s_lpLua, baseIndex + 3);
		P3 arg3 = LuaDataType<P3>::get(s_lpLua, baseIndex + 4);
		P4 arg4 = LuaDataType<P4>::get(s_lpLua, baseIndex + 5);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0, arg1, arg2, arg3, arg4);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5>
	int LuaClass<Class>::createObject_6(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);
		P1 arg1 = LuaDataType<P1>::get(s_lpLua, baseIndex + 2);
		P2 arg2 = LuaDataType<P2>::get(s_lpLua, baseIndex + 3);
		P3 arg3 = LuaDataType<P3>::get(s_lpLua, baseIndex + 4);
		P4 arg4 = LuaDataType<P4>::get(s_lpLua, baseIndex + 5);
		P5 arg5 = LuaDataType<P5>::get(s_lpLua, baseIndex + 6);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0, arg1, arg2, arg3, arg4, arg5);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}
	template<typename Class>
	template<typename P0, typename P1, typename P2, typename P3, typename P4, typename P5, typename P6>
	int LuaClass<Class>::createObject_7(lua_State* lpState)
	{
		int baseIndex = 0;
		if(s_lpLua->getStack()->isTable(1))
			baseIndex = 1;

		// Get the Arguments
		P0 arg0 = LuaDataType<P0>::get(s_lpLua, baseIndex + 1);
		P1 arg1 = LuaDataType<P1>::get(s_lpLua, baseIndex + 2);
		P2 arg2 = LuaDataType<P2>::get(s_lpLua, baseIndex + 3);
		P3 arg3 = LuaDataType<P3>::get(s_lpLua, baseIndex + 4);
		P4 arg4 = LuaDataType<P4>::get(s_lpLua, baseIndex + 5);
		P5 arg5 = LuaDataType<P5>::get(s_lpLua, baseIndex + 6);
		P6 arg6 = LuaDataType<P6>::get(s_lpLua, baseIndex + 7);

		// Create the Object
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(s_lpLua->getStack()->requestUserData(sizeof(LuaGCObject<Class>)));
		gcObject->Object = new Class(arg0, arg1, arg2, arg3, arg4, arg5, arg6);
		gcObject->ShouldBeCollected = true;

		// Set the Meta Table
		s_MetaTable.push();
		s_lpLua->getStack()->setMetaTable(-2);

		return 1;
	}

	template<typename Class>
	template<typename R>
	int LuaClass<Class>::unaryMinus(lua_State* lpState)
	{
		Class object = LuaDataType<Class>::get(s_lpLua, -1);
		
		R newObj = -object;
		LuaDataType<R>::push(s_lpLua, newObj);

		return 1;
	}
	template<typename Class>
	template<typename R, typename OtherClass>
	int LuaClass<Class>::additionOperator(lua_State* lpState)
	{
		Class object = LuaDataType<Class>::get(s_lpLua, -2);
		OtherClass otherObject = LuaDataType<OtherClass>::get(s_lpLua, -1);

		R newObj = object + otherObject;
		LuaDataType<R>::push(s_lpLua, newObj);

		return 1;
	}
	template<typename Class>
	template<typename R, typename OtherClass>
	int LuaClass<Class>::subtractionOperator(lua_State* lpState)
	{
		Class object = LuaDataType<Class>::get(s_lpLua, -2);
		OtherClass otherObject = LuaDataType<OtherClass>::get(s_lpLua, -1);

		R newObj = object - otherObject;
		LuaDataType<R>::push(s_lpLua, newObj);

		return 1;
	}
	template<typename Class>
	template<typename R, typename OtherClass>
	int LuaClass<Class>::multiplicationOperator(lua_State* lpState)
	{
		Class object = LuaDataType<Class>::get(s_lpLua, -2);
		OtherClass otherObject = LuaDataType<OtherClass>::get(s_lpLua, -1);

		R newObj = object * otherObject;
		LuaDataType<R>::push(s_lpLua, newObj);

		return 1;
	}
	template<typename Class>
	template<typename R, typename OtherClass>
	int LuaClass<Class>::divisionOperator(lua_State* lpState)
	{
		Class object = LuaDataType<Class>::get(s_lpLua, -2);
		OtherClass otherObject = LuaDataType<OtherClass>::get(s_lpLua, -1);

		R newObj = object / otherObject;
		LuaDataType<R>::push(s_lpLua, newObj);

		return 1;
	}
	template<typename Class>
	template<typename R, typename IndexType>
	int LuaClass<Class>::indexOperator(lua_State* lpState)
	{
		Class object = LuaDataType<Class>::get(s_lpLua, -2);
		IndexType index = LuaDataType<IndexType>::get(s_lpLua, -1);

		R = object[index];
		LuaDataType<R>::push(s_lpLua, newObj);

		return 1;
	}
	template<typename Class>
	template<typename R, typename IndexType>
	int LuaClass<Class>::newIndexOperator(lua_State* lpState)
	{
		Class& object = LuaDataType<Class&>::get(s_lpLua, -3);
		IndexType index = LuaDataType<IndexType>::get(s_lpLua, -2);
		R value = LuaDataType<R>::get(s_lpLua, -1);

		object[index] = value;

		return 0;
	}

	template<typename Class>
	int LuaClass<Class>::deleteObject(lua_State* lpState)
	{
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(LuaStack::getUserData(lpState, -1));
		if(gcObject->ShouldBeCollected)
			delete gcObject->Object;

		return 0;
	}
	template<typename Class>
	int LuaClass<Class>::forceDestroyObject(lua_State* lpState)
	{
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(LuaStack::getUserData(lpState, -1));
		delete gcObject->Object;

		return 0;
	}
	template<typename Class>
	int LuaClass<Class>::keepObject(lua_State* lpState)
	{
		LuaGCObject<Class>* gcObject = reinterpret_cast<LuaGCObject<Class>*>(LuaStack::getUserData(lpState, -1));
		gcObject->ShouldBeCollected = false;

		return 0;
	}

}

#endif